<?php
/**
 * Form validation class. Validates form fields' using Zend_Validate.
 * Forms are described in xml files. This class makes php validation 
 * and builds js code for client side validation.
 * (jQuery library http://bassistance.de/jquery-plugins/jquery-plugin-validation/ should be used)
 * To link srver side rule name and cliend side rule names config/validator.ini file is used.
 * 
 * @author Konstantin Shamko <konstantin.shamko@gmail.com> 
 * @version 0.0.1
 * @copyright  Copyright (c) 2009 Konstantin Shamko
 * @category VaselinEngine
 * @package Bel Classes
 * @license  New BSD License
 *
 */
class Bel_Forms_Validator {
	
	/**
	 * Name of a validated form
	 *
	 * @var string
	 */
	private $formname;
	
	/**
	 * Path to a dir with xml files which describes forms
	 *
	 * @var string
	 */
	private $rule_path;
	
	/**
	 * Array with validation errors
	 *
	 * @var array
	 */
	private $errors = array ( );
		
	/**
	 * Class constructor. Sets path to the rules dir
	 *
	 * @param string $rule_path - path to the rules dir. Will be set to a defaul value if empty. 
	 */
	function __construct($rule_path = '') {
		if ($rule_path == ''){
			$this->rule_path = HOME_DIR . 'config/forms/';
		} else{
		
			$this->rule_path = $rule_path;
		}
	}	
	
	/**
	 * Call this to validate a form
	 *
	 * @param string $formname - name of a validated form
	 * @param array $to_validate - request array to validate
	 * @return bool - true(if ok)/false (if suxxx)
	 */
	public function validate($formname, $to_validate) {
		
		$formdata = $this->_getFormData ( $formname );		
		$assoc = new Zend_Config_Ini(HOME_DIR.'config/validator.ini','production');
		foreach ($formdata as $field){
			if($field['required'] || (!$field['required'] && @$to_validate[$field['name']])){
				$valid = new Zend_Validate();
				$valid->addValidator(new Zend_Validate_NotEmpty());
				foreach ($field['rules'] as $rule){
					
					if($rule['rule']==='maxlength'){
						$_vld = new $assoc->$rule['rule'](NULL, $rule['value']);
						$valid->addValidator($_vld->setMessages(array(Zend_Validate_StringLength::TOO_LONG=>$rule['message'])));
					}elseif($rule['rule']==='minlength'){
						$_vld = new $assoc->$rule['rule']($rule['value'], NULL);
						$valid->addValidator($_vld->setMessages(array(Zend_Validate_StringLength::TOO_SHORT=>$rule['message'])));
					}else{
						$_vld = new $assoc->$rule['rule']();
						$valid->addValidator($_vld->setMessage($rule['message']));
					}					
				}
				
				if(isset($to_validate[$field['name']])){ //if field is in form then validate.
					if(!$valid->isValid($to_validate[$field['name']])){
						$messages = $valid->getMessages();
						foreach ($messages as &$message){
							$this->errors[$field['name']][] = $message;
							unset($message);
						}	
					}				
				}
				unset($valid);
				
			}
		}
		
		if (count ( $this->errors ) === 0){
			return true;
		} else{
			return false;
		}
	}	
	
	/**
	 * Generates js validation code based on jquery.validate.js library
	 *
	 * @param string $formname - name of the validated form
	 */
	public function generateValidateJS($formname) {
		$formdata = $this->_getFormData ( $formname );
		
		$rules = 'rules:{';
		$messages = 'messages:{';

		if(count($formdata)){
			foreach ($formdata as $field){
				$_rule=$field['name'].":{required:".(string)(($field['required']===true)?"true":"false");
				$_message = $field['name'].":{required:".(string)(($field['required']===true)?"'This field is required'":"''");
				if(count($field['rules'])){
					$_rule.=',';
					$_message.=',';
					
					foreach($field['rules'] as $vld){
						$_rule.=$vld['rule'].':'.(string)(($vld['value'])?$vld['value'].',':'true,');
						$_message.=$vld['rule'].':"'.(string)$vld['message'].'",';
					}	
					$_rule = rtrim($_rule,',');
					$_message = rtrim($_message,',');
				}
				
				$_rule.='}';
				$_message.='}';
				
				$msg[] = $_message;
				$rl[] = $_rule;
			}
				
			$messages.=implode(',',$msg);
			$rules.=implode(',',$rl); 
		}
		
		$rules.='}';
		$messages.='}';
		
		echo "$('#$formname').validate({".$rules.", ".$messages."})";
	}	
	
	/**
	 * Class getter. It is needed to get validation errors 
	 *
	 * @return array - array with errors
	 */
	public function getErrors() {
		return $this->errors;
	}	
	
	/**
	 * Parses file with form description 
	 *
	 * @param string $formname - name of the validated form
	 * @return array $fields - array with form data in an appropriate format
	 */
	private function _getFormData($formname) {
		$xmlfile = $this->rule_path . $formname . ".xml";	
		$fields = array();
		
		if(file_exists($xmlfile)){
			$xml = simplexml_load_file($xmlfile);		
			
			foreach($xml->fields->field as $field){			
				$tmp = (array)$field->validations;
				$rules = array();
				if(isset($tmp['validation'])){
					
					$validations = array();
					if(!is_array($tmp['validation'])){
						$validations[] = $tmp['validation']; 
					}else{
						$validations = $tmp['validation'];
					};
					
					foreach ($validations as $validation){
						$rules[] = array('rule'=>(string)$validation->rule,'value'=>(isset($validation->value))?(string)$validation->value:NULL,'message'=>(string)$validation->message);
					}
				}
				$fields[] = array('name'=>(string)$field->name,'required'=>($field->validations && (!$field->attributes()->required||$field->attributes()->required=='true'))?true:false,'rules'=>$rules);
			}
		}
		return $fields;	
	}
}